import os
import shutil
import requests
import music_tag
import PIL
import io
from PyQt5.QtCore import QObject, pyqtSignal
from yandex_music.exceptions import NetworkError


invalid_characters_windows = "<>”\"/\\|?*.:"


class Downloader(QObject):
    finished = pyqtSignal()

    def __init__(self, main_window, playlists, save_path, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.main_window = main_window
        self.playlists = playlists
        self.save_path = save_path
        self.music_client = self.main_window.music_client

    def run(self):
        # Ensure required directories exist
        self.create_save_paths()

        # Organize tracks into playlists
        sorted_tracks = {}
        for playlist in self.playlists:
            tracks = self.music_client.get_tracks_from_playlist(playlist)
            sorted_tracks[playlist["title"]] = tracks

        # Calculate total tracks for progress bar
        tracks_amount = sum(len(tracks) for tracks in sorted_tracks.values())
        downloaded_tracks = 0

        # Set progress bar range
        self.main_window.progress_bar.setMinimum(0)
        self.main_window.progress_bar.setValue(0)
        self.main_window.progress_bar.setMaximum(tracks_amount)

        # Download and save tracks
        for playlist_title, tracks in sorted_tracks.items():
            playlist_path = os.path.join(self.save_path, playlist_title)
            if not os.path.exists(playlist_path):
                os.mkdir(playlist_path)

            for track in tracks:
                corrected_track_title = track.title.replace("/", "").replace("\\", "")
                track_artists = [artist.name for artist in track.artists]
                corrected_track_artists = ",".join(track_artists).replace("/", "").replace("\\", "")

                if os.name == "nt":
                    for invalid_character in invalid_characters_windows:
                        corrected_track_title = corrected_track_title.replace(invalid_character, "")
                        corrected_track_artists = corrected_track_artists.replace(invalid_character, "")

                save_path = os.path.join(playlist_path, f"{corrected_track_title} - {corrected_track_artists}.mp3")
                temp_path = os.path.join(
                    self.save_path, "temp", f"{corrected_track_title} - {corrected_track_artists}.mp3")

                if not os.path.exists(save_path):
                    self.download_track(track, temp_path, save_path)

                downloaded_tracks += 1
                self.main_window.progress_bar.setValue(downloaded_tracks)

        # Remove temporary directory after downloading
        shutil.rmtree(os.path.join(self.save_path, "temp"))
        self.finished.emit()

    def create_save_paths(self):
        # Create main save_path and temporary directory if not exist
        if not os.path.exists(self.save_path):
            os.mkdir(self.save_path)

        temp_path = os.path.join(self.save_path, "temp")
        if not os.path.exists(temp_path):
            os.mkdir(temp_path)

    def download_track(self, track, temp_path, save_path):
        # Download track from Yandex.Music and save to temporary path
        url = self.music_client.get_track_url(track.id)
        response = requests.get(url)
        content = response.content

        with open(temp_path, "wb") as mp3:
            mp3.write(content)

        # Set metadata tags and move the track to its final location
        self.set_music_tags(track, temp_path)
        os.rename(temp_path, save_path)

    def set_music_tags(self, track, temp_path):
        # Set metadata tags (title, artist, album) for the track
        track_title = track.title
        track_artists = [artist.name for artist in track.artists]
        track_albums = [album.title for album in track.albums]

        mp3 = music_tag.load_file(temp_path)

        mp3.append_tag("title", track_title)
        mp3.append_tag("artist", ",".join(track_artists))
        mp3.append_tag("album", ",".join(track_albums))

        # Download and set track cover (artwork) if available
        if track.cover_uri:
            self.download_and_set_cover(track, mp3)

        mp3.save()

    def download_and_set_cover(self, track, mp3):
        # Download track cover image and set it as artwork in the metadata
        cover_path = os.path.join(self.save_path, "temp", f"{track.title}.png")

        if os.name == "nt":
            for invalid_character in invalid_characters_windows:
                cover_path = cover_path.replace(invalid_character, "")

        while True:
            try:
                track.download_cover(cover_path, "400x400")
                break
            except NetworkError:
                continue

        img = PIL.Image.open(cover_path, mode="r")
        img_bytes_array = io.BytesIO()
        img.save(img_bytes_array, format="PNG")
        img_bytes_array = img_bytes_array.getvalue()

        mp3.append_tag("artwork", img_bytes_array)
        os.remove(cover_path)
